package com.cn.lp.jpa.cq.domain;

import com.cn.lp.common.utils.CodingAide;
import com.cn.lp.jpa.cq.CQConditionPredicate;
import com.cn.lp.jpa.cq.CQuery;
import com.cn.lp.log.LogUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;

import javax.annotation.Nullable;
import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

class CQueryImpl<TFrom> implements CQuery<TFrom>, Specification<TFrom> {

    /**
     * 数据源类型
     */
    private Class<TFrom> fromClass;

    /**
     * 条件
     */
    private CQConditionPredicate<TFrom> condition;

    /**
     * 顺序
     */
    private List<CQOrder<TFrom>> orders;

    private String signKey;

    private CQueryImpl() {

    }

    static <TFrom> CQueryImpl<TFrom> of(Class<TFrom> fromClass, @Nullable CQConditionPredicate<TFrom> condition,
        List<CQOrder<TFrom>> orders, String signKey) {
        var result = new CQueryImpl<TFrom>();
        result.fromClass = fromClass;
        result.condition = condition;
        result.orders = orders;
        result.signKey = signKey;
        return result;
    }

    @Override
    public Specification<TFrom> toSpecification() {
        return this;
    }

    @Override
    public String getSign() {
        return CodingAide.MD5(signKey);
    }

    @Override
    public String getSign(Pageable pageable) {
        return CodingAide.MD5(LogUtils.format("{},limit{}-{}", signKey, pageable.getPageNumber(), pageable.getPageSize()));
    }

    @SuppressWarnings({"Duplicates", "NullableProblems"})
    @Override
    public Predicate toPredicate(Root<TFrom> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        if (Objects.nonNull(condition)) {
            query.where(condition.toPredicate(root, query, cb));
        }
        if (!orders.isEmpty()) {
            var os = new ArrayList<Order>();
            for (var order : orders) {
                os.add(order.toOrder(root, query, cb));
            }
            query.orderBy(os);
        }
        return query.getRestriction();
    }

}
